package com.base.util;

import com.base.util.intfac.CountConvert;
import org.apache.commons.lang.StringUtils;

import java.util.*;
import java.util.function.BiConsumer;

/**
 * Created by Shisan on 2017/2/14.
 */
public class NodeCreateUtil {

    /**
     * 该方法是将对象转换为拥有上下级关系的Map集合
     * @param originalList 要被转换的平行list集合，该集合中的元素可以是普通对象，也可以是Map集合，甚至集合类型不同都没有关系。普通对象必须包含被指定的属性名，而map中必须包含被指定的key
     * @param field 需要被转换的列表。该参数必须被指定。 其中可以使用 别名=原始名的方式指定属性。 field不能为空，否则应用程序不执行解析操作
     *              其中，Map自身会携带一些参数
     *              在map集合中，会自身携带一些参数：id，parentId,children
     *              其中可以使用pid=parentId这样的方式为之起别名。允许的格式：pid=parentId,pid= 和pid 三种格式
     *              而id是必须值，如果未传入，则自动启用id。对象中必须包含id字段。
     *
     *              2017.4.25 重新设计：
     *              将使用两个Map作为索引，一个key为parentId，另一个为id，结果集存在listResult中。每次获取的值将在两个索引Map中查找并添加。
     *
     */
    public static List<?> createContextNode(List<?> originalList,String... field){
        if(field == null || field.length == 0){
            return null;
        }
        if(originalList == null || originalList.size() == 0){
            return null;
        }
        //该集合存放了 原生属性名=别名的键值对
        Map<String,String> fieldAN = new HashMap<>();
        fieldAN = new HashMap<>();

        //解析field
        int index;
        for(String str:field){
            str = str.trim();
            if((index=str.indexOf("="))!=-1 && index < str.length()-1){
                fieldAN.put(str.substring(index+1),str.substring(0,index));
            }else{
                if(index == -1){
                    fieldAN.put(str,str);
                }else{
                    str = str.substring(0,index);
                    if(StringUtils.isNotBlank(str))
                        fieldAN.put(str,str);
                }
            }
        }
        if(fieldAN.get("parentId") == null){
            fieldAN.put("parentId","parentId");
        }
        if(fieldAN.get("id") == null){
            fieldAN.put("id","id");
        }
        if(fieldAN.get("children") == null){
            fieldAN.put("children","children");
        }
        Map<Object,Map<Object,Object>> idIndexMap = new LinkedHashMap<>(); //id-value 根据id的索引集合
        Map<Object,List<Map<Object,Object>>> parentIdIndexMap = new HashMap<>(); //id-list<value> 根据parentId的索引集合

        for(Object obj:originalList) {
            Map<Object, Object> tempMap = new HashMap<>();
            for (String fieldName : fieldAN.keySet()) {
                if (obj instanceof Map) {
                    Map<Object, Object> map = (Map<Object, Object>) obj;
                    tempMap.put(fieldAN.get(fieldName), map.get(fieldAN.get(fieldName)));
                } else {
                    try {
                        tempMap.put(fieldAN.get(fieldName), ReflectUtil.getReadMethodByField(ReflectUtil.getFieldByName(fieldAN.get(fieldName),obj.getClass())).invoke(obj));
                    } catch (Exception e) {
                    }
                }
            }
            Object id = tempMap.get(fieldAN.get("id"));
            Object parentId = tempMap.get(fieldAN.get("parentId"));
            idIndexMap.put(id, tempMap); //将所有的id存入该索引map中。
            Map<Object, Object> tempFrom = idIndexMap.get(parentId); //根据当前元素的父id获取元素
            if (tempFrom != null) { //如果元素可以获取到，说明当前元素属于该元素的子元素。应当将当前的元素存入到获取到的元素的子元素中。
                List<Map<Object, Object>> listMap = (List<Map<Object, Object>>) tempFrom.get(fieldAN.get("children"));
                if (listMap == null) {
                    listMap = new ArrayList<>();
                    tempFrom.put(fieldAN.get("children"), listMap);
                }
                listMap.add(tempMap);
            } else {
                //将所有元素的父id存入到该map索引中
                List<Map<Object, Object>> listTempFrom = parentIdIndexMap.get(parentId);
                if (listTempFrom == null) {
                    listTempFrom = new ArrayList<>();
                    parentIdIndexMap.put(parentId, listTempFrom);
                }

                listTempFrom.add(tempMap);
            }


            //根据当前元素的id查找子元素
            List<Map<Object, Object>> listChildren = parentIdIndexMap.get(id);
            //如果根据当前ID可以找到子元素，则将这些元素添加到子元素的子属性中
            if (listChildren != null) {
                tempMap.put(fieldAN.get("children"), listChildren);
            }
        }
        //return getRootNode(idIndexMap.values().stream().collect(Collectors.toList()),fieldAN.get("id"),fieldAN.get("parentId"));
        List<Object> list = new ArrayList<>();
        list.addAll(idIndexMap.values());
        return getRootNode(list,fieldAN.get("id"),fieldAN.get("parentId"));
    }

    /**
     * 循环遍历具备上下级关系的集合，该集合来自createContextNode的转换结果。
     * 请注意，使用该方法遍里时，会自动为Map添加一个属性 _level，它记录当前节点的级别。请确保Map中没有该_level属性，否则会导致值覆盖问题。
     * _level从0开始
     * _level在doWork之前已经被存入，所以可以在doWork方法中获取_level的值
     * @param listMap
     * @param doWork
     */
    public static void foreachContextNode(List<Map<Object,Object>> listMap,BiConsumer<Integer,Map<Object,Object>> doWork){

        foreachContextNode(listMap,doWork,new int[]{0},0);

    }

    /**
     * 循环遍历具备上下级关系的集合，该集合来自createContextNode的转换结果。
     * 这是一个私有方法，如果有必要可以改为public，它用于记录当前节点属于第几级别。最顶级级别的level为0.
     * 请注意，使用该方法遍里时，会自动为Map添加一个属性 _level，它记录当前节点的级别。请确保Map中没有该_level属性，否则会导致值覆盖问题。
     * index为循环下标，从0开始 为了index的准确性，这里使用数组对象引用，获取index[0]即为当前的index.
     * @param listMap
     * @param doWork
     */
    private static void foreachContextNode(List<Map<Object,Object>> listMap, BiConsumer<Integer,Map<Object,Object>> doWork,int []index, int level){
        List<Map<Object,Object>> childMap;
        for(Map<Object,Object> map:listMap){
            map.put("_level",level);
            if(doWork!=null)
                doWork.accept(index[0]++,map);
            else index[0]++;
            if((childMap= ( List<Map<Object,Object>> ) map.get("children"))!=null){
                foreachContextNode(childMap,doWork,index,level+1);
            }
        }

    }

    /**
     * 将具备上下级关系的listMap转换为平行关系
     * 其中，children属性将会被移除。
     * @param listResult 要保存的对象
     * @param listMap 被转换对象
     * @param childrenName 转换依据 子节点
     */
    public static void contextToSliderNode(List<Map<Object,Object>> listResult,List<Map<Object,Object>> listMap,String childrenName){
        if(listMap == null)
            return ;
        Map<Object,Object> toMap;
        for(Map<Object,Object> map:listMap){
            toMap  = new HashMap<>();
            for(Object key:map.keySet()){
                if(childrenName.equals(key)){
                    continue;
                }
                toMap.put(key,map.get(key));
            }
            listResult.add(toMap);
            contextToSliderNode(listResult,(List<Map<Object,Object>>)map.get(childrenName),childrenName);
        }

    }

    //这里返回的Object用于标识是否完成添加。如果Object返回空表示未添加成功，不为空则添加成功。
    //如果返回空，则你可以直接在listResult中添加node了。因为并没有在指定的listResult中找到和node关联的位置，表示node属于顶级的并列节点了。

    /**
     * 为List<Map>>添加父/子节点，
     * @param listResult 已创建的list
     * @param node 要添加的node，注意该node必须是单子节点，它目前不可以有下级，否则如果node属于listResult父节点时，listResult会替换掉node的原来子节点。
     * @param idName node中的id名称
     * @param pidName node中的parentId名字
     * @param childrenName node中的children的名字
     * @return 如果添加成功返回值不为空，添加失败返回值为空
     */
    public static Object addContextNode(List<Map<Object,Object>> listResult,Map<Object,Object> node,String idName,String pidName,String childrenName){

        Object id;
        Object parentId;
        List<Map<Object, Object>> children;

        for(Map<Object,Object> map:listResult){

            //获取当前信息，
            id = map.get(idName);
            parentId = map.get(pidName);
            children = (List<Map<Object, Object>>) map.get(childrenName);
            if(children == null){
                children = new ArrayList<>();
                map.put(childrenName,children);
            }else if(children.size() > 0){
                if(addContextNode(children,node,idName,pidName,childrenName)!=null){
                    return "";
                }
            }
            //如果被添加的node属于当前被遍历的子机构，则添加

            if(id!= null && id.equals(node.get(pidName))){
                children.add(node);
                return "";
            }else if(parentId != null && parentId.equals(node.get(idName))){ //如果被遍历的节点属于node下属节点，则添加，一般情况下，被遍历的node是当前的顶级节点。
                children = new ArrayList<>();
                children.addAll(listResult);
                node.put(childrenName,children);
                listResult.clear();
                listResult.add(node);
                return "";
            }

        }

        return null;
    }

    /**
     *  平行未分级的集合，添加列，边添加边计算被添加进去的数量，并显示在每一个标签上。
     * 请注意，此方法只适合统计最后一级数据，也就是说新增的数据不会被统计。统计完成后会添加到数据
     * 一定要注意，被统计的父节点不能有被统计的字段，如果有则会出现意想不到的错误
     * @param listResult
     * @param nodeList
     * @param idName
     * @param pidName
     * @param fieldNames
     */
    public static void countAddSliderNodeForLast(List<Map<Object,Object>> listResult, List<Map<Object,Object>> nodeList, String idName, String pidName, CountConvert<Object,Object> p, String...fieldNames){
        countSliderNodeForLast(listResult,nodeList,idName,pidName,p,fieldNames);
        listResult.addAll(nodeList);
    }

    /**
     * 平行未分级的集合，只，并显示在每一个标签上。
     * 请注意，此方法只适合统计最后一级数据，也就是说新增的数据不会被统计。
     * @param listResult
     * @param nodeList
     * @param fieldNames
     */
    public static void countSliderNodeForLast(List<Map<Object,Object>> listResult, List<Map<Object,Object>> nodeList, String idName, String pidName, CountConvert<Object,Object> p, String...fieldNames){

        List<Map<Object,Object>> temp1 = new LinkedList<>();
        List<Map<Object,Object>> temp2 = new LinkedList<>();
        temp1.addAll(listResult);
        temp2.addAll(nodeList);
        Object lpid,tid;
        Map<Object,Object> ltempObj,ttempObj;
        Map<Object,Object> lastValue = new HashMap<>();
        //先比对新增的数据，将非新增的最底层数据进行一次数据基数添加。
        for(int i=0;i<temp2.size();){ //取新增数据
            ltempObj = temp2.get(i);
            lpid = ltempObj.get(pidName);
            for(int j=0;j<temp1.size();j++){ //取原来数据
                ttempObj = temp1.get(j);
                tid=ttempObj.get(idName);
                if(tid.equals(lpid)){ //新增父id与原来子id相同
                    if(fieldNames != null && fieldNames.length > 0 && p!=null){
                        for(String fieldName:fieldNames){
                            //ttempObj是原来数据
                            //ltempObj是新增数据
                            ttempObj.put(fieldName,p.apply(ttempObj.get(fieldName) == null,fieldName,ttempObj.get(fieldName),ltempObj.get(fieldName),ttempObj,ltempObj));
                        }
                    }
                    continue;
                }
            }
            temp2.remove(i);
        }


        //该部分是后续优化后修正的部分： 2017/3/2
        int countSize = -1;
        while(countSize != temp1.size()){
            countSize = temp1.size();

            if(countSize > 0){
                List<Integer> inte = countMethod(temp1,temp1.get(0),idName,pidName);
                if(inte != null){
                    //先找到父机构
                    Map<Object,Object> map = temp1.get(inte.get(0));
                    Map<Object,Object> parent = null;
                    for(Map<Object,Object> pa:temp1){
                        if(pa.get(idName).equals(map.get(pidName))){
                            parent = pa;
                            break;
                        }
                    }
                    if(parent == null){ //没有父机构那就没必要再计算了。
                        break;
                    }
                    boolean b = true;
                    for(Integer index:inte){
                        map = temp1.get(index);
                        for(String fieldName:fieldNames){
                            parent.put(fieldName,p.apply(b,fieldName,parent.get(fieldName),map.get(fieldName),parent,map));
                        }
                        if(b){
                            b = false;
                        }
                    }

                    //移除已经计算过的最底层的元素
                    //Collections.sort(inte,(o1,o2)->o1-o2);

                    Collections.sort(inte, new Comparator<Integer>() {
                        @Override
                        public int compare(Integer o1, Integer o2) {
                            return o1 - o2;
                        }
                    });

                    for(int i=0;i<inte.size();i++){
                        temp1.remove(inte.get(i)-i);
                    }
                }
            }
        }
    }

    /**
     * 查找指定节点的子节点，找到该节点的最底部的子节点，返回这些节点所在list的index值。其中list集合是平行级结合，不存在上下级关系。
     * 如果被查找的节点已经是最底层节点，和它同级别的其他子节点存在下级子节点，则会去查找同级别的下级子节点，直到找到最顶层为止。
     * 2017/3/2
     * @param list
     * @param map
     */
    private static List<Integer> countMethod(List<Map<Object,Object>> list,Map<Object,Object> map,String idName,String pidName){
        Map m;
        boolean isNotFound = true; //是否发现还有子机构
        for(int i = 0;i< list.size();i++){
            m = list.get(i);
            if(map.get(idName).equals(m.get(pidName))){
                return countMethod(list,m,idName,pidName);
            }
        }
        //已经查找到最底层节点的第一步，接下来查找与该节点同级的其他节点，如果其他节点存在子节点，则去查找该子节点。
        List<Integer> temp = new ArrayList<>();
        for(int i = 0;i< list.size();i++){
            m = list.get(i);
            //查找同级别的其他节点。
            if(!m.get(idName).equals(map.get(idName))&&((m.get(pidName) == null && map.get(pidName) == null) || (m.get(pidName)!=null && m.get(pidName).equals(map.get(pidName))) || (map.get(pidName)!=null && map.get(pidName).equals(m.get(pidName))))){
                for(Map<Object,Object> tm:list){
                    if(m.get(idName).equals(tm.get(pidName))){
                        return countMethod(list,tm,idName,pidName);
                    }
                }
                temp.add(i);
            }else if(m.get(idName).equals(map.get(idName))){
                temp.add(i);
            }
        }
        return temp;

    }

    /**
     * 如果值为空，则返回默认值
     * @param t
     * @param def 默认值
     * @return
     */
    public static Object getOrDefault(Object t,Object def){
        return t==null?def:t;
    }

    /**
     * 查找当前list中最顶级的节点。
     * @param originalList
     * @param idName
     * @param parentIdName
     * @return
     */
    public static List<?> getRootNode(List<?> originalList,String idName,String parentIdName){
        List<Object> list = new ArrayList<>();
        list.addAll(originalList);

        Object obj1,obj2,id1,id2,parentId1,realParentObj,realParentId;
        Map<Object,Object> tempMap;
        Map<Object,Object> parentIndexMap = new LinkedHashMap<>();
        there:for(int i=0;i<list.size();i++){
            obj1 = list.get(i);
            if(obj1 instanceof Map){
                tempMap = (Map<Object, Object>) obj1;
                id1 = tempMap.get(idName);
                parentId1 = tempMap.get(parentIdName);
            }else{
                try {
                    id1 = ReflectUtil.getReadMethodByField(ReflectUtil.getFieldByName(idName,obj1.getClass())).invoke(obj1);
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
                try {
                    parentId1 = ReflectUtil.getReadMethodByField(ReflectUtil.getFieldByName(parentIdName,obj1.getClass())).invoke(obj1);
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
            realParentId = id1;
            realParentObj = obj1;
            for(int j=0;j<list.size();j++){
                if(i == j){
                    continue;
                }
                obj2 = list.get(j);
                if(obj2 instanceof Map){
                    tempMap = (Map<Object, Object>) obj2;
                    id2 = tempMap.get(idName);
                }else{
                    try {
                        id2 = ReflectUtil.getReadMethodByField(ReflectUtil.getFieldByName(idName,obj2.getClass())).invoke(obj2);
                    } catch (Exception e) {
                        e.printStackTrace();
                        continue;
                    }
                }
                if(id2.equals(parentId1)){  //obj2是obj1的父节点
                    continue there;
                }
            }
            if(parentIndexMap.get(realParentId) == null)
                parentIndexMap.put(realParentId,realParentObj);
        }
        list.clear();
        for(Object m:parentIndexMap.values()){
            list.add(m);
        }
        return list;
    }

    public static void main(String args[]) throws Exception{

        List<Object> list = new ArrayList<>();
        Map<Object,Object> m1 = new HashMap<>();
        m1.put("id",1);
        m1.put("name","ceshi");
        list.add(m1);
        m1 = new HashMap<>();
        m1.put("id",2);
        m1.put("name","ceshi2");
        list.add(m1);
        m1 = new HashMap<>();
        m1.put("id",3);
        m1.put("parentId",1);
        m1.put("name","ceshi3");
        list.add(m1);
        m1 = new HashMap<>();
        m1.put("id",4);
        m1.put("parentId",2);
        m1.put("name","ceshi");
        list.add(m1);
        m1 = new HashMap<>();
        m1.put("id",5);
        m1.put("parentId",4);
        m1.put("name","ceshi5");
        list.add(m1);
        m1 = new HashMap<>();
        m1.put("id",6);
        m1.put("parentId",5);
        m1.put("name","ceshi6");
        list.add(m1);

        List list2 = NodeCreateUtil.createContextNode(list,"name");

        System.out.println(list2);

    }

}
